module material.mipmap;

import canvas.abstractcanvas;
import canvas.floatcanvas;
import std.math;
import utils.vector2;
import utils.color;

void mipmapRect(uint mipmap_level, uint size, out Vector2i p1, out int nsize) {
    if (mipmap_level == 0) {
        p1 = Vector2i(0,0);
        nsize = size;
    } else {
        p1 = Vector2i(size, 0);
        nsize = size/(1<<mipmap_level);
        if (nsize < 2)
            nsize = 2;
        p1.y = size - 2*nsize;
    }
}

//generates an image like seen in Wikipedia:
//  http://upload.wikimedia.org/wikipedia/de/4/44/MipMap_Beispiel_STS101.jpg
//tex must be quadratic and have a length that is a power of 2
AbstractCanvas generateMipMap(AbstractCanvas tex) {
    uint nsize = tex.width;
    assert(tex.height == nsize);
    AbstractCanvas ntex = new FloatCanvas(tex.size+Vector2i(nsize/2,0));
    //sigh
    for (int y = 0; y < nsize; y++) {
        for (int x = 0; x < nsize; x++) {
            auto c = tex.getPixelColor(Vector2i(x, y));
            ntex.drawPixel(Vector2i(x, y), c);
        }
    }
    Vector2i psrc = Vector2i(0,0);
    Vector2i pdst = Vector2i(nsize, 0);
    while (nsize > 1) {
        std.stdio.writefln("%d",nsize);
        for (int y = 0; y < nsize; y += 2) {
            for (int x = 0; x < nsize; x += 2) {
                auto c1 = ntex.getPixelColor(psrc+Vector2i(x, y));
                auto c2 = ntex.getPixelColor(psrc+Vector2i(x+1, y));
                auto c3 = ntex.getPixelColor(psrc+Vector2i(x, y+1));
                auto c4 = ntex.getPixelColor(psrc+Vector2i(x+1, y+1));
                auto c = (c1+c2+c3+c4)*0.25f;
                ntex.drawPixel(pdst+Vector2i(x/2, y/2), c);
            }
        }
        psrc = pdst;
        pdst.y += nsize/2;
        nsize = nsize/2;
    }
    return ntex;
}

//positive levels for x axis, negative for y axis
void ripmapRect(int level_x, int level_y, uint size, out Vector2i p1, out Vector2i s) {
    uint nsize_x = size/(1<<level_x);
    uint nsize_y = size/(1<<level_y);
    if (nsize_x < 2)
        nsize_x = 2;
    if (nsize_y < 2)
        nsize_y = 2;

    p1.x = size*2 - nsize_x*2;
    p1.y = size*2 - nsize_y*2;
    s.x = nsize_x;
    s.y = nsize_y;
}

// http://www.3dcenter.de/images/grafikfilter/ripmaps.png  <-- falsch :D
AbstractCanvas generateRipMap(AbstractCanvas tex) {
    assert(tex.width == tex.height);
    AbstractCanvas ntex = new FloatCanvas(tex.size*2);
    int nsize = tex.width;
    for (int y = 0; y < nsize; y++) {
        for (int x = 0; x < nsize; x++) {
            auto c = tex.getPixelColor(Vector2i(x, y));
            ntex.drawPixel(Vector2i(x, y), c);
        }
    }

    Vector2i psrc = Vector2i(0,0);
    Vector2i pdst = Vector2i(nsize, nsize);
    while (nsize > 1) {
        std.stdio.writefln("%d",nsize);

        int rsize = nsize;

        void doCompute(Vector2i delegate(Vector2i p) perm, int old_offs, int offs) {
            //tex.width or tex.height (depends), same value
            for (int r = 0; r < nsize; r++) {
                for (int side = 0; side < rsize; side++) {
                    RGBColor c1 = ntex.getPixelColor(psrc+perm(Vector2i(old_offs+side*2, r)));
                    RGBColor c2 = ntex.getPixelColor(psrc+perm(Vector2i(old_offs+side*2+1, r)));
                    RGBColor c = (c1 + c2) * 0.5f;
                    ntex.drawPixel(psrc+perm(Vector2i(offs+side, r)), c);
                }
            }
        }

        int offs = nsize;
        int old_offs = 0;
        while (rsize > 1) {
            rsize = rsize/2;
            std.stdio.writefln(rsize);
            doCompute((Vector2i d) {return d;}, old_offs, offs);
            doCompute((Vector2i d) {return Vector2i(d.y, d.x);}, old_offs, offs);
            old_offs = offs;
            offs = offs+rsize;
        }

        for (int y = 0; y < nsize; y += 2) {
            for (int x = 0; x < nsize; x += 2) {
                auto c1 = ntex.getPixelColor(psrc+Vector2i(x, y));
                auto c2 = ntex.getPixelColor(psrc+Vector2i(x+1, y));
                auto c3 = ntex.getPixelColor(psrc+Vector2i(x, y+1));
                auto c4 = ntex.getPixelColor(psrc+Vector2i(x+1, y+1));
                auto c = (c1+c2+c3+c4)*0.25f;
                ntex.drawPixel(pdst+Vector2i(x/2, y/2), c);
            }
        }
        psrc = pdst;
        pdst.x += nsize/2;
        pdst.y += nsize/2;
        nsize = nsize/2;
    }

    return ntex;
}

//summed area table
FloatCanvas generateSAT(AbstractCanvas tex) {
    auto res = new FloatCanvas(tex.size);
    auto p = Vector2i(0,0);
    res.drawSafePixel(p, tex.getPixelColor(p));
    for (int y = 0; y < tex.size.y; y++) {
        for (int x = 0; x < tex.size.x; x++) {
            RGBColor col = tex.getPixelColor(Vector2i(x, y));
            RGBColor tmp;
            if (x>0) {
                res.getPixelColor(Vector2i(x-1, y),tmp);
                col += tmp;
            }
            if (y>0) {
                res.getPixelColor(Vector2i(x, y-1),tmp);
                col += tmp;
            }
            if (x>0 && y>0) {
                res.getPixelColor(Vector2i(x-1, y-1),tmp);
                col -= tmp;
            }
            res.drawPixel(Vector2i(x, y), col);
        }
    }
    return res;
}

RGBColor getSATValue(AbstractCanvas sat, Vector2i p0, Vector2i p1) {
    int x0 = p0.x>p1.x?p1.x:p0.x-1;
    int x1 = p0.x>p1.x?p0.x:p1.x;
    int y0 = p0.y>p1.y?p1.y:p0.y-1;
    int y1 = p0.y>p1.y?p0.y:p1.y;
    float size = fmax(1.0f,cast(float)(x1-x0)*(y1-y0));
    RGBColor col0, col1, col2, col3;
    if (x0>=0 && y0>=0)
        col0 = sat.getPixelColor(Vector2i(x0,y0));
    if (x0>=0)
        col1 = sat.getPixelColor(Vector2i(x0,y1));
    if (y0>=0)
        col2 = sat.getPixelColor(Vector2i(x1,y0));
    col3 = sat.getPixelColor(Vector2i(x1,y1));
    //std.stdio.writefln("%s",(col3 + col0 - col2 - col1)*invSize);
    return (col3 + col0 - col2 - col1)*(1.0f/size);
}
